package ldh.im.base;

import io.netty.channel.Channel;
import io.netty.channel.ChannelId;
import ldh.im.ImConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

public class SessionManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
    private static SessionManager instance;

    private ConcurrentHashMap<ChannelId, ImSession> channelMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<Long, ImSession> userImSessionMap = new ConcurrentHashMap<>();
    private Set<TimeChannel> removeChannels = new HashSet();
    private ScheduledExecutorService removeChannelExecutor = Executors.newScheduledThreadPool(1);

    private SessionManager() {
        // 定时10分钟清理一次需要移出的channel
//        removeChannelExecutor.scheduleAtFixedRate(this::timerRemoveChannel, 0, 10, TimeUnit.SECONDS);
    }

    public static SessionManager getInstance() {
        if (instance == null) {
            synchronized (SessionManager.class) {
                if (instance == null) {
                    instance = new SessionManager();
                }
            }
        }
        return instance;
    }

    public void close() {
        removeChannelExecutor.shutdownNow();
    }

    public ImSession getImSession(Channel channel) {
        LOGGER.info("session size: " + channelMap.size());
        ChannelId channelId = channel.id();

        ImSession imSession = channelMap.get(channelId);
        if (!channel.isActive()) {
            channelMap.remove(channel);
        }
        return imSession;
    }

    public ImSession addChannel(Channel channel) {
        ChannelId channelId = channel.id();
        if (channelMap.contains(channelId)) return channelMap.get(channelId);
        ImSession imSession = new DefaultImSession(channel);
        int hashCode = (int) (Math.random() * Integer.MAX_VALUE);
        imSession.put(ImConfig.USER_HASHCODE, hashCode);
        imSession.put(ImConfig.CHANNEL_ID, channelId);
        channelMap.putIfAbsent(channelId, imSession);
        return channelMap.get(channel);
    }

    public ImSession remove(Channel channel) {
        ChannelId channelId = channel.id();
        ImSession imSession = channelMap.remove(channelId);
        return imSession;
    }

    private static final class TimeChannel {
        private long removeTime;
        private ChannelId channelId;

        public TimeChannel(long removeTime, ChannelId channelId) {
            this.removeTime = removeTime;
            this.channelId = channelId;
        }

        @Override
        public boolean equals(Object object) {
            if (object == null) return false;
            if (!(object instanceof TimeChannel)) return false;
            TimeChannel newTimeChannel = (TimeChannel) object;
            return newTimeChannel.getChannelId().equals(this.getChannelId());
        }

        @Override
        public int hashCode() {
            return channelId.hashCode();
        }

        public long getRemoveTime() {
            return removeTime;
        }

        public ChannelId getChannelId() {
            return channelId;
        }
    }

    private void timerRemoveChannel() {
        List<TimeChannel> removes = new ArrayList();
        long now = System.currentTimeMillis();
        for(TimeChannel timeChannel : removeChannels) {
            if (now - timeChannel.getRemoveTime() >= 2 * 60L * 1000L * 1000L) {
                removes.add(timeChannel);
                channelMap.remove(timeChannel.getChannelId());
            }
        }
        removeChannels.remove(removes);

    }
}
